/*
 *  Copyright 2011 Iurii Dunko, Iana Potochniak.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.yyminijava.symboltable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.opu.yyminijava.syntaxtree.ClassBody;
import org.opu.yyminijava.syntaxtree.ClassDeclaration;
import org.opu.yyminijava.syntaxtree.ClassType;
import org.opu.yyminijava.syntaxtree.FieldDeclaration;
import org.opu.yyminijava.syntaxtree.FormalParameter;
import org.opu.yyminijava.syntaxtree.FormalParameterList;
import org.opu.yyminijava.syntaxtree.IdentifierList;
import org.opu.yyminijava.syntaxtree.MethodBody;
import org.opu.yyminijava.syntaxtree.MethodDeclaration;
import org.opu.yyminijava.syntaxtree.MethodDeclarator;
import org.opu.yyminijava.syntaxtree.MethodHeader;
import org.opu.yyminijava.syntaxtree.Modifiers;
import org.opu.yyminijava.syntaxtree.Name;

/**
 *
 * @author Iurii Dunko
 * @author Iana  Potochniak
 */
public class NativeClassRecord extends ClassRecord {

    public NativeClassRecord(Class<?> clazz) {
        super(clazz.getSimpleName());
        setType(getClassType(clazz));

        ClassDeclaration cd = new ClassDeclaration();
        cd.setModifiers(new Modifiers());
        cd.setName(clazz.getSimpleName());
        cd.setClassBody(getClassBody(clazz));

        Class<?> superClass = clazz.getSuperclass();
        if(superClass != null){
            NativeClassRecord superN = new NativeClassRecord(superClass);
            cd.setSuperClass(superN.getValue());
        }

        setValue(cd);
    }

    private ClassType getClassType(Class<?> clazz){
        if ("void".equals(clazz.getName())){
            return null;
        }
        return new ClassType(new Name(clazz.getSimpleName()));
    }

    private ClassBody getClassBody(Class<?> clazz){
        ClassBody cb = new ClassBody();
        cb.setFields(getFields(clazz));
        cb.setMethods(getMethods(clazz));
        return cb;
    }

    private List<FieldDeclaration> getFields(Class<?> clazz)
            throws SecurityException {

        List<FieldDeclaration> fields = new ArrayList<FieldDeclaration>();

        for (Field field : clazz.getFields()) {
            ClassType ft = getClassType(field.getType());
            Modifiers ms = new Modifiers();
            IdentifierList idL = new IdentifierList();
            idL.addName(field.getName());
            // NOTE: int.class.getName()  = int
            fields.add(new FieldDeclaration(ms, ft, idL));
        }

        return fields;
    }

    private List<MethodDeclaration> getMethods(Class<?> clazz)
            throws SecurityException {

        List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>();

        for (Method cm : clazz.getMethods()) {
            MethodDeclaration m = new MethodDeclaration();

            MethodHeader mh = new MethodHeader();
            mh.setModifiers(new Modifiers());
            mh.setType(getClassType(cm.getReturnType()));

            MethodDeclarator md = new MethodDeclarator();
            md.setSimpleName(cm.getName());

            FormalParameterList fpl = null;
            int i = 1;
            for (Class<?> prm : cm.getParameterTypes()) {
                if (fpl == null){
                    fpl = new FormalParameterList();
                }
                ClassType tp = getClassType(prm);
                fpl.addParameter(new FormalParameter(tp, "arg" + i++));
            }
            md.setParameterList(fpl);

            mh.setDeclarator(md);

            m.setHeader(mh);
            m.setBody(new MethodBody());

            methods.add(m);
        }

        return methods;
    }
}
